Skip to content

Conversation

@pajawojciech
Copy link

Add search to manager orders

Adds search box to find orders by job name + arrow indicators to show which one you're looking at.

What it does

  • Search box appears on orders screen (Alt+S to focus)
  • Type keywords to filter (e.g. "iron picks" finds "forge iron picks")
  • Multi-word search works in any order ("picks iron" also matches)
  • Alt+P/N to jump between matches
  • Shows "3 of 12" style counter
  • Green >> arrows point at current match
  • Arrows hide when you scroll manually

Implementation

  • Uses stockflow reaction names for search (see Fix user-friendly job names so that they are the same as in the manager menu scripts#1524 for upcoming names update to match in-game text)
  • Two overlays: OrdersSearchOverlay for search UI, OrderHighlightOverlay for arrows
  • Both disabled by default until stockflow names are updated to match in-game text
  • Moved importexport overlay position to make room for search - users with saved overlay settings need to run: overlay position orders.importexport default

What didn't work

  • Tried to clear search input when exiting orders window - couldn't find reliable way to detect window exit

Related issues

order-search.mp4

Adds search overlay to find and navigate manager orders with arrow indicators showing current search result. Search uses Alt+S to focus, Alt+P/N for prev/next navigation. Overlays are disabled by default.
Comment on lines +749 to +757
return string.format('%d:%d:%d:%d:%d:%s:%s:%s',
order.job_type,
order.item_type,
order.item_subtype,
order.mat_type,
order.mat_index,
order.reaction_name or '',
mat_cat_str,
encrust_str)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this results in lexicographic ordering by the text representation of job type's number, which means 0, 1, 10, 11, 12, ..., 19, 2, 20, 21...? while this is a stable sort order, it is not an order the player will likely anticipate

Copy link
Contributor

@ChrisJohnsen ChrisJohnsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nice workaround for the inability to filter the view like in the other info panel search widgets.

I like the labeled magic numbers in OrderHighlightOverlay. DF's layout behavior is not fun thing to have to replicate, but I think this would be fairly clear to someone that hasn't looked at this stuff as much (at least as long as they have DF at hand to try out different window sizes).

Re: not being able to detect exiting the window: yeah, I have also wanted something
like a callback for "this overlay is no longer active (not going to be rendered)".


  • Would utils.search_text be useful here? The main SortOverlay uses it for searching.
  • Could the first match automatically be highlighted on Enter?
    I can see how you might not want to highlight (thus likely move the scroll position) for each change in search input, but Enter seems like a nice place to jump to the first match.
  • The highlight should probably be cleared when the search text changes (especially when the highlighted order does not match the new search input).
  • It might just be my color vision being flaky, but I completely did not notice the highlight arrow at first. Now that I know what to look for, it isn't hard to spot. But my first cycling through the matches was confusing because it wasn't obvious which order was being indicated.

Comment on lines -77 to +82
default_pos={x=53,y=-6},
default_pos={x=41,y=-6},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new default position can be forced by setting/changing the overlay's version, but that will also re-enable this overlay for anyone that has previously disabled it, so it isn't always appropriate. But it may be better than players having to seek out how to adjust/reset the overlay's position.

Comment on lines +808 to +811
default_pos={x=85, y=-6},
default_enabled=false,
viewscreens='dwarfmode/Info/WORK_ORDERS/Default',
frame={w=34, h=4},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This combination of position and size does not properly fit in a minimum-size DF interface (114x46 UI cells, 912x552px with the default 8x12 UI font). The x-offset of 84 plus width of 34 puts the right edge at 118 columns.

This isn't a problem in "wide" DF windows, but if the the interface ends up narrower than 118 (due to window size and/or DF interface scaling percentage), then the overlay.search window will be pushed to the left. In a minimum-width interface this will cause the orders.search and orders.importexport windows (at their default positions established in this commit) to overlap by three columns (which one is on top varies across startups due to the Lua pairs iteration done for overlay rendering).

The leftward push experienced while the DF interface is narrower than 118 columns will remain until the overlay is reloaded (e.g., the player reloads the fort (via overlay's onStateChange handler), or :lua require'plugins.overlay'.rescan()). Only the widget's frame table is affected, not its persisted overlay configuration.


I don't know if it is standard procedure for DFHack (I'm just a contributor here), but I always try out overlay stuff in a variety of window sizes (especially a minimum-size window, which seems to heavily influences DF's own layout, and thus DFHack's DF-relative overlays) to make sure things will fit.

For this overlay, it looks like it could be narrowed by four columns without affecting its utility too much. Actually, I would recommend narrowing by six columns to keep the right edge away from the right edge of the (minimum-width) interface area (which will cause issues if gui/overlay is used to move the overlay window in a minimum-width interface: it will automatically swap to a right-anchored position which (in wider interfaces) will cause the overlay to "float" to the right instead of "sticking with" the (default position) of the orders.importexport overlay window).

desc='Shows arrows next to the work order found by orders.search',
default_enabled=false,
viewscreens='dwarfmode/Info/WORK_ORDERS/Default',
frame={w=80, h=3},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be full_interface=true instead of using a frame. This prevents an "phantom window" from being configurable in gui/overlay.

return math.floor(available_height / self.ORDER_HEIGHT)
end

function OrderHighlightOverlay:calculateSelectedOrderY()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a bug here when deleting an order above the highlighted order. Here is a way to reproduce the problem:

  1. Have more than one screen-full (page) of orders (i.e., the scrollbar is active).

  2. Have a searchable order at or near the bottom of the the last page of orders.

  3. Search for the order.

  4. Move highlight to the order that is on the last page.

    The list should now be scrolled to the bottom of the list and there should be other orders above the highlighted order.

  5. Delete an order above the highlighted order.

    The highlight is now drawn on the wrong row (or not drawn if it was "pushed off the bottom").


You might fix this by canceling the highlight (and invalidating the search -- or re-doing the search to update the match count) if the number of orders changes.

return map
end

local reaction_map_cache = nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept noticing a stutter when doing my first search of a bootup (I would type "plant" and end up with "pnt"). I think it is this cache being populated with ~3500 entries. Not sure if anything can be done to smooth that out. Maybe I am just running on a potato?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3500 entries in a lua map is not something we want to be doing lightly

OrderHighlightOverlay.super.render(self, dc)
end

function OrderHighlightOverlay:onRenderFrame(dc, rect)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It probably isn't a big deal, but there doesn't seem to be a reason to have both render and onRenderFrame? Putting everything in one (probably render, since this is rightfully a full_interface overlay widget without any frame) worked okay when I tried it briefly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants